/*
 * Copyright (C) 2009 Ionut Dediu <deionut@yahoo.com>
 *
 * Licensed under the GNU General Public License Version 2
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

// CamFilter.cpp

#include "CamFilter.h"
#include "SmartCamConst.h"
#include "SmartCamAppUi.h"
#include "SmartCamSettingsData.h"

CCamFilter* CCamFilter::NewL()
    {
    CCamFilter* self = NewLC();
    CleanupStack::Pop(self);
    return self;
    }

CCamFilter* CCamFilter::NewLC()
    {
    CCamFilter* self =
		new (ELeave) CCamFilter();
    CleanupStack::PushL(self);
    self->ConstructL();
    return self;
    }

CCamFilter::CCamFilter():
	iStreamMgr(NULL),
	iCommFilter(NULL),
	iJpegEncoder(NULL),
	iCamera(NULL)
    {
    }

CCamFilter::~CCamFilter()
    {
    if(iCamera != NULL)
    	{
    	delete iCamera;
    	iCamera = NULL;
    	}
    if(iJpegEncoder != NULL)
    	{
    	delete iJpegEncoder;
    	iJpegEncoder = NULL;
    	}
    }

void CCamFilter::ConstructL()
    {
    iJpegEncoder = new(ELeave) CJpegEncoder(KJpegQualityFactor);
    }

// From MSmartFilter
void CCamFilter::Open(MStreamManager* aMgr)
	{
	iStreamMgr = aMgr;
	iCamera = CCamera::NewL(*this, ((CSmartCamAppUi*) iStreamMgr)->GetSettingsData().CameraSrcIndex());
	iCamera->Reserve();
	}

void CCamFilter::AttachCommFilter(CCommFilter* aCommFilter)
	{
	iCommFilter = aCommFilter;
	}

void CCamFilter::Start()
	{
	TSize size(160, 120);
	// Start the viewfinder.
	iCamera->StartViewFinderBitmapsL(size);
	TDesC8& tables = iJpegEncoder->SetQuality(KJpegQualityFactor);
	if(tables.Length() == 0)
		{
		TDesC& msg = iJpegEncoder->GetErrorString();
		iStreamMgr->OnFilterError(this, KErrUnknown, msg);
		}
	else
		{
		iCommFilter->SendPacket(EPacketTypeJPEGHeader, tables);
		}

	TInt sizeIdx = ((CSmartCamAppUi*) iStreamMgr)->GetSettingsData().CameraResolutionIndex();
	TInt fpsIdx = ((CSmartCamAppUi*) iStreamMgr)->GetSettingsData().CameraFPSIndex();
	iCamera->PrepareVideoCaptureL(CCamera::EFormatYUV420Planar, sizeIdx, fpsIdx, 2, 1);
	iCamera->StartVideoCapture();
	}

void CCamFilter::Close()
	{
	iCamera->StopViewFinder();
	iCamera->StopVideoCapture();
	iCamera->Release();
	delete iCamera;
	iCamera = NULL;
	}

void CCamFilter::EnumerateVideoResolutions(TInt aCameraIndex, RArray<TSize>& aArray)
	{
	aArray.Reset();
	CCamera* camera = CCamera::NewL(*this, aCameraIndex);
	TCameraInfo camInfo;
	camera->CameraInfo(camInfo);
	for(TInt sizeIdx = 0; sizeIdx < camInfo.iNumVideoFrameSizesSupported; sizeIdx++)
		{
		TSize videoFrameSize;
		camera->EnumerateVideoFrameSizes(videoFrameSize, sizeIdx, CCamera::EFormatYUV420Planar);
		if(videoFrameSize.iWidth != 0)
			aArray.Append(videoFrameSize);
		}
	delete camera;
	}

void CCamFilter::EnumerateVideoFrameRates(TInt aCameraIndex, TInt aResolutionIndex, RArray<TReal32>& aArray)
	{
	aArray.Reset();
	CCamera* camera = CCamera::NewL(*this, aCameraIndex);
	TCameraInfo camInfo;
	camera->CameraInfo(camInfo);
	for(TInt fpsIdx = 0; fpsIdx < camInfo.iNumVideoFrameRatesSupported; fpsIdx++)
		{
		TReal32 crtFps = 0;
		camera->EnumerateVideoFrameRates(crtFps, fpsIdx, CCamera::EFormatYUV420Planar, aResolutionIndex);
		if(crtFps != 0)
			aArray.Append(crtFps);
		}
	delete camera;
	}

TInt CCamFilter::CamerasAvailable()
	{
	return CCamera::CamerasAvailable();
	}

// From MCameraObserver:
void CCamFilter::ReserveComplete(TInt aError)
	{
	if(aError == KErrNone)
		{
		// Once the camera has been successfully reserved, theW application will power up the camera.
		iCamera->PowerOn();
		}
	else
		{
		TBuf<60> errMsg;
		errMsg.Format(_L("Could not reserve camera %d"), aError);
		iStreamMgr->OpenComplete(this, aError, errMsg);
		}
	}

void CCamFilter::PowerOnComplete(TInt aError)
	{
	if(aError == KErrNone)
		{
		iStreamMgr->OpenComplete(this, KErrNone);
		}
	else
		{
		iCamera->Release();
		TBuf<60> errMsg;
		errMsg.Format(_L("Could not power on camera %d"), aError);
		iStreamMgr->OpenComplete(this, aError, errMsg);
		}
	}

void CCamFilter::ViewFinderFrameReady(CFbsBitmap& aFrame)
	{
	((CSmartCamAppUi*)(iStreamMgr))->DisplayFrame(&aFrame);
	}

void CCamFilter::ImageReady(CFbsBitmap* /* aBitmap */, HBufC8* /* aData */, TInt /* aError */) {}

void CCamFilter::FrameBufferReady(MFrameBuffer* aFrameBuffer , TInt aError)
	{
	if(aError != KErrNone)
		{
		if(aFrameBuffer != NULL)
			aFrameBuffer->Release();

		TBuf<60> errMsg;
		errMsg.Format(_L("Camera error %d"), aError);
		iStreamMgr->OnFilterError(this, aError, errMsg);
		return;
		}
	if(iCommFilter->CanAcceptMore())
		{
		TSize frameSize;
		iCamera->GetFrameSize(frameSize);
		TDesC8& jpeg = iJpegEncoder->Encode(*aFrameBuffer->DataL(0), frameSize.iWidth, frameSize.iHeight);
		if(jpeg.Length() == 0)
			{
			TDesC& msg = iJpegEncoder->GetErrorString();
			iStreamMgr->OnFilterError(this, KErrUnknown, msg);
			}
		else
			{
			iCommFilter->SendPacket(EPacketTypeJPEGData, jpeg);
			}
		}
	aFrameBuffer->Release();
	}

